/*
 * Disk I/O operations based on ATRDLL.DLL
 */

#include "dis6502.h"
#include <errno.h>

/*
 * Seek to a specific sector in the file.
 */
static ImgError ImageSeek(FILE *fd, LPImgInfo pInfo, LPImgRWPacket pSector)
{
DWORD dwOffset;
WORD wDensity;

     /*
      * check sector number
      */                   
     if ((pSector->wSectorNumber < 1) || (pSector->wSectorNumber > pInfo->wSectors))
          return IMG_OUT_OF_RANGE;

     /*
     ** if user wants to read one of the three first sector of a double
     ** density disk, the size should be only 128 bytes.
     */
     if ((pInfo->wDensity == 256) && (pSector->wSectorNumber <= 3))
          wDensity = 128;
     else wDensity = pInfo->wDensity;          

     /*
      * determine offset in the file
      */
     switch (pInfo->nResult)
          {
          case IMG_XFD:
               dwOffset = (DWORD) (pSector->wSectorNumber - 1) * (DWORD) wDensity;
               break;

          case IMG_ATR:
               dwOffset = (DWORD) sizeof(ATRHeader) + ((DWORD) (pSector->wSectorNumber - 1) * (DWORD) wDensity);
               break;
          }

     /*
      * go to sector
      */
     if (fseek(fd, (long) dwOffset, SEEK_SET))
          return IMG_DISK_ERROR;

     /*
      * everything is OK.
      */
     return pInfo->nResult;
}

/*
 * read an absolute sector (128 or 256 bytes).
 */
static ImgError ImageRead(LPImgInfo pInfo, LPImgRWPacket pSector)
{
ImgError iRet;
FILE *fd;

     /*
      * open Atari disk file.
      */
     if ((fd = fopen(pSector->lpszFileName, "rb")) == NULL)
          return IMG_FILE_NOT_FOUND;

     /*
      * go to sector.
      */
     if ((iRet = ImageSeek(fd, pInfo, pSector)) < 0)
          {
          fclose(fd);
          return iRet;
          }

     /*
      * the sector is read.
      */
     if (fread(pSector->cSectorData, pInfo->wDensity, 1, fd) != 1)
          iRet = IMG_DISK_ERROR;
     else iRet = pInfo->nResult;

     /*
     ** returns result.
     */
     fclose(fd);
     return iRet;
}

/*
 * write an absolute sector (128 or 256 bytes).
 */
static ImgError ImageWrite(LPImgInfo pInfo, LPImgRWPacket pSector)
{
ImgError iRet;
FILE *fd;

     /*
      * open Atari disk file.
      */
     if ((fd = fopen(pSector->lpszFileName, "r+b")) == NULL)
          {
          if (errno == EACCES)
               return IMG_WRITE_PROTECT;
          else return IMG_FILE_NOT_FOUND;
          }

     /*
      * go to sector.
      */
     if ((iRet = ImageSeek(fd, pInfo, pSector)) < 0)
          {
          fclose(fd);
          return iRet;
          }

     /*
      * the sector is read.
      */
     if (fwrite(pSector->cSectorData, pInfo->wDensity, 1, fd) != 1)
          iRet = IMG_DISK_ERROR;
     else iRet = pInfo->nResult;

     /*
     ** returns result.
     */
     fclose(fd);
     return iRet;
}

/*
 * Get information about the disk image (sector size, disk type,...)
 */
void ImageGetInfo(LPCSTR pFilename, LPImgInfo pInfo)
{
struct _stat buf;
ATRHeader Atr;
unsigned long lSectorCount;
int fd;

     /*
      * does the file exist ?
      */
     if (_stat(pFilename, &buf))
          pInfo->nResult = IMG_FILE_NOT_FOUND;
     else {

          /*
          ** by default, the file is not recognized.
          */
          pInfo->nResult = IMG_BAD_MAGIC;

          /*
          ** check if the file is write-protected.
          */
          pInfo->cWriteProtect = ((buf.st_mode & _S_IWRITE) ? FALSE : TRUE);

          /*
          ** check if this is an ATR file
          */
          if ((fd = open(pFilename, _O_BINARY | _O_RDONLY)) > 0)
               {
               if (read(fd, &Atr, sizeof(Atr)) == sizeof(Atr))
                    if ((Atr.nMagic1 == ATR_MAGIC1) && (Atr.nMagic2 == ATR_MAGIC2))
                         {
                         pInfo->nResult = IMG_ATR;
                         pInfo->wDensity = (WORD) Atr.nSecSizeLo + ((WORD) Atr.nSecSizeHi << 8);
                         lSectorCount = (unsigned long) Atr.nSecCountLo + ((unsigned long) Atr.nSecCountHi << 8)
                                      + ((unsigned long) Atr.nHugeSecCountLo << 16) + ((unsigned long) Atr.nHugeSecCountHi << 24);
                         lSectorCount >>= 3;
                         if (pInfo->wDensity == 256)
                              {
                              lSectorCount += 3;
                              lSectorCount >>= 1;
                              }
                         pInfo->wSectors = (WORD) lSectorCount;
                         close(fd);
                         return;
                         }
               close(fd);
               }

          /*
          ** check if this can be an XFD file
          */
          switch (buf.st_size)
               {

               /*
               ** single density
               */
               case 40 * 18 * 128L:
                    pInfo->nResult = IMG_XFD;
                    pInfo->wSectors = 40 * 18;
                    pInfo->wDensity = 128;
                    break;

               /*
               ** enhanced density
               */
               case 40 * 26 * 128L:
                    pInfo->nResult = IMG_XFD;
                    pInfo->wSectors = 40 * 26;
                    pInfo->wDensity = 128;
                    break;

               /*
               ** Indus GT double density
               */
               case 40 * 18 * 256L:
                    pInfo->nResult = IMG_XFD;
                    pInfo->wSectors = 40 * 18;
                    pInfo->wDensity = 256;
                    break;

               /*
               ** Quad density
               */
               case 80 * 18 * 256L:
                    pInfo->nResult = IMG_XFD;
                    pInfo->wSectors = 80 * 18;
                    pInfo->wDensity = 256;
                    break;
               }
          }
}

/*
 * Read a sector from a disk image.
 */
void ImageReadSector(LPImgRWPacket pSector)
{
ImgInfo Info;

     /*
      * Get image info to know sector size, disk type,...and call ImageRead
      */
     ImageGetInfo(pSector->lpszFileName, &Info);
     if (Info.nResult < 0)
          pSector->nResult = Info.nResult;
     else pSector->nResult = ImageRead(&Info, pSector);
}

/*
 * Write a sector to a disk image.
 */
void ImageWriteSector(LPImgRWPacket pSector)
{
ImgInfo Info;

     /*
      * Get image info to know sector size, disk type,...and call ImageWrite
      */
     ImageGetInfo(pSector->lpszFileName, &Info);
     if (Info.nResult < 0)
          pSector->nResult = Info.nResult;
     else pSector->nResult = ImageWrite(&Info, pSector);
}

/*
 * Display an error returned by disk image read/write functions
 */
ImgError ImageError(HWND hWnd, ImgError err)
{
     switch (err)
          {
          case IMG_XFD:             /* XFD Image Detected */
          case IMG_ATR:             /* ATR Image Detected */
               break;

          case IMG_BAD_MAGIC:       /* File does not have a NICKATARI Signature */
               Error(hWnd, IDS_ERR_IMG_BAD_MAGIC, "ATR file does not have a NICKATARI signature");
               break;

          case IMG_FILE_NOT_FOUND:  /* File does not exist */
               Error(hWnd, IDS_ERR_IMG_FILE_NOT_FOUND, "File does not exist");
               break;

          case IMG_OUT_OF_RANGE:    /* Sector out of range */
               Error(hWnd, IDS_ERR_IMG_OUT_OF_RANGE, "Sector out of range");
               break;

          case IMG_DISK_ERROR:      /* Disk error occured */
               Error(hWnd, IDS_ERR_IMG_DISK_ERROR, "Disk error occured");
               break;

          case IMG_WRITE_PROTECT:   /* Denied: Write protect error */
               Error(hWnd, IDS_ERR_IMG_WRITE_PROTECT, "Disk image is write protected");
               break;
          }
     return err;
}
